package com.service.creditcard.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.client.common.config.IdWorker;
import com.client.model.pojo.bank.ArBankcard;
import com.client.model.pojo.creditcard.ConsumeRecord;
import com.client.model.pojo.creditcard.CreditCard;
import com.client.model.pojo.user.ArUser;
import com.client.model.vo.common.Result;
import com.client.model.vo.common.ResultCodeEnum;
import com.client.model.vo.creditLoanVo.CreditCardVo;
import com.service.creditcard.feignService.BankCardFeignService;
import com.service.creditcard.feignService.UserFeignCreditCardService;
import com.service.creditcard.service.ConsumeRecordService;
import com.service.creditcard.service.CreditCardService;
import com.service.creditcard.mapper.CreditCardMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author liuwenju
 * @description 针对表【credit_card】的数据库操作Service实现
 * @createDate 2024-04-01 20:57:00
 */
@Slf4j
@Service
public class CreditCardServiceImpl extends ServiceImpl<CreditCardMapper, CreditCard>
        implements CreditCardService {
    @Autowired
    private CreditCardMapper creditCardMapper;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ConsumeRecordService consumeRecordService;
    @Autowired
    private BankCardFeignService bankCardFeignService;
    @Autowired
    private UserFeignCreditCardService userFeignCreditCardService;

    @Override
    public Result<Page<CreditCard>> getCreditCard(Integer page, Integer limit, String token) {
        log.info("token---------->{}", token);

        ArUser user = JSON.parseObject(redisTemplate.opsForValue().get("user:login" + token), ArUser.class);
        log.info("user--------->{}", user);

        QueryWrapper<CreditCard> cardQueryWrapper = new QueryWrapper<>();
        assert user != null;
        cardQueryWrapper.eq("uid", user.getUid());
        Page<CreditCard> creditCardPage = creditCardMapper.selectPage(new Page<>(page, limit), cardQueryWrapper);
        return Result.build(creditCardPage, ResultCodeEnum.SUCCESS);
    }

    @Override
    public Result rebackMoney(CreditCardVo creditCardVo) {
        log.info("creditCardVo-------->{}", creditCardVo);
        //用户还款操作 该方法应该由分布式事务完成

        //还应当注意还款金额不能大于已借金额
        //首先去匹配当前银行卡中的金钱是否大于用户输入的金额（是否可以完成扣款操作）---》应该由前端去完成

        //通过传入的cid去查找当前信用卡信息
        CreditCard creditCard = this.creditCardMapper.selectById(creditCardVo.getCid());
        log.info("还款操作的该张信用卡信息----->{}", creditCard);
        //并将uid所在的用户表中找寻用户的真实姓名与用户输入的vo类中的用户id进行匹配
        ArUser arUser = this.userFeignCreditCardService.getUserById(creditCard.getUid()).getData();
        log.info("还款操作的该张信用卡所属用户，用来验证信息是否正确------------》{}", arUser);
        //用户表中的真实姓名与vo类中的真实姓名一致，并且银行卡中的真实姓名与用户表中的一致则证明是该用户本人的操作
        if (!arUser.getRealName().equals(creditCardVo.getRealName())) {
            return Result.build(null, ResultCodeEnum.DATA_ERROR);
        }
        //判断是否是本人操作
        ArBankcard arBankcard = this.bankCardFeignService.getCreditCardById(creditCardVo.getBid()).getData();
        log.info("还款操作的该张信用卡所要还款的银行卡扣款账户------------>{}", arBankcard);
        if (!arBankcard.getRealName().equals(creditCardVo.getRealName())) {
            return Result.build(null, ResultCodeEnum.DATA_ERROR);
        }
        //判断用户应该还款的金额数，先扣除银行卡中的钱，然后再填补上信用卡中使用的部分
        //subtract  该方法是使用BigDecimal的减法
        if (arBankcard.getBalance().compareTo(creditCardVo.getMoney()) < 0) {
            //银行卡中的钱不能够支持本次还款操作
            return Result.build(null, ResultCodeEnum.DATA_ERROR);
        }
        //先扣除银行卡中的钱
        arBankcard.setBalance(arBankcard.getBalance().subtract(creditCardVo.getMoney()));
        //更新银行卡表
        if (this.bankCardFeignService.update(arBankcard).getCode() != 200) {
            //扣款失败，事务回滚
            return Result.build(null, ResultCodeEnum.DATA_ERROR);
        }
        //银行卡表更新成功，则更新信用卡表
        //将透支金额更新成还款之后的金额数，透支金额减少---
        //利息按今天开始到本月月初之间的天数来计算还款金额
        log.info("扣款之后的借款金额是多少钱----------->{}", creditCard.getBorrowCount().subtract(creditCardVo.getMoney()));
        creditCard.setBorrowCount(creditCard.getBorrowCount().subtract(creditCardVo.getMoney()));
        int i = this.creditCardMapper.updateById(creditCard);
        if (i != 1) {
            throw new RuntimeException("信用卡还款失败");
        }


        //还款记录表的操作---------------------------------------------------------------
        QueryWrapper<ConsumeRecord> consumeRecordQueryWrapper = new QueryWrapper<>();
        consumeRecordQueryWrapper.eq("remark", 0);
        consumeRecordQueryWrapper.eq("card_num", creditCard.getCardNum());
        //根据升序排序，先还清最先借款的订单
        consumeRecordQueryWrapper.orderByAsc("consume_date");
        List<ConsumeRecord> consumeRecordList = this.consumeRecordService.list(consumeRecordQueryWrapper);
        //遍历当前集合判断所还金额涉及到了几个订单（如果还清则给订单设置为1，证明该订单已还清
        //如果当前还款金额小于订单金额，则给当前订单拆分成两个：一个为还清订单，一个为未还订单）

        //还款金额是按照当前订单消费金额（本金）+自消费日起到今天所产生的利息来计算（利息）
        // 如果还款金额不足这两个之和，那么优先还取利息，再还取剩余金额
        for (ConsumeRecord c : consumeRecordList) {
            if (creditCardVo.getMoney().compareTo(new BigDecimal(0)) <= 0) {
                //证明当次循环中。用户的还款金额已经用完，弹出for循环
                break;
            }
            log.info("还款操作中当次的consumeList----------------->{}", c);
            //todo 当前订单是否是按时间顺序排序？

            //计算最早的一笔订单到如今所差天数，并计算利息
            long between = DateUtil.between(c.getConsumeDate(), new Date(), DateUnit.DAY);
            log.info("当前订单是否逾期------------->{}", between);
            if (between > 30) {
                //如果超过一个月30天，则当前订单被视为逾期，按本金的日利率万分之五计息(欠款天数)，日利率*天数*本金
                BigDecimal bigDecimal = new BigDecimal((between - 30L) * 0.0005);
                log.info("需还取的利率为----------{}", bigDecimal);
                BigDecimal rateMoney = c.getMoney().multiply(bigDecimal);
                log.info("订单逾期所需还取利息--------------->{}", rateMoney);
                //如果等于1则证明当前还款金额小于利息，直接返回，不予执行还款操作
                if (rateMoney.compareTo(creditCardVo.getMoney()) >= 0) {
                    throw new RuntimeException("还款金额不足以还取最低还款额！");
                } else {
                    //如果大于0那么首先还取利息，在判断剩余金额是否可以用来支付剩余的金额。
                    //如果不能，则将该订单拆分，并且修改还款利息记录，以防止在下次还款中，重复还款某些日期的利息。
                    BigDecimal subtract = creditCardVo.getMoney().subtract(rateMoney);
                    log.info("本息还取利息之后的剩余资金--------->{}",subtract);
                    if (subtract.compareTo(c.getMoney()) > 0) {
                        //则证明还款金额大于订单金额，直接还款
                        //设置当前订单为还清状态
                        ConsumeRecord consumeRecord = new ConsumeRecord();
                        consumeRecord.setRates(rateMoney);
                        consumeRecord.setCardNum(c.getCardNum());
                        consumeRecord.setId(c.getId());
                        consumeRecord.setConsumeDate(c.getConsumeDate());
                        consumeRecord.setRemark(1);
                        consumeRecord.setMoney(c.getMoney());
                        boolean b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                        if (!b) {
                            throw new RuntimeException("还款失败");
                        }
                        //当前传进来的还款金额=还款金额-(当前订单本金+利息)
                        creditCardVo.setMoney(subtract.subtract(c.getMoney()));
                        log.info("经过逾期还款之后，还清当笔账单之后剩余的金额--------->{}",creditCardVo.getMoney());
                    } else {
                        // 则证明还款金额小于订单消费金额，则拆分订单为已还金额订单，还应还取金额订单
                        // 并更新未还取订单金额日期为当前日期，避免利息重复扣款

                        //新增订单还清表
                        ConsumeRecord consumeRecord = new ConsumeRecord();
                        consumeRecord.setConsumeDate(c.getConsumeDate());
                        consumeRecord.setMoney(subtract);
                        consumeRecord.setRates(rateMoney);
                        consumeRecord.setRemark(1);
                        IdWorker idWorker = new IdWorker();
                        consumeRecord.setId(idWorker.nextId());
                        consumeRecord.setCardNum(c.getCardNum());

                        //当小于消费金额时，则证明当前金额无力偿还下一张的账单
                        creditCardVo.setMoney(new BigDecimal(0));

                        boolean b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                        if (!b) {
                            throw new RuntimeException("还款失败");
                        }
                        //修改订单为未还表
                        consumeRecord.setRemark(0);
                        //无需重复计算已还日期的利息(消费日为刚满一个月的逾期第一天日期)
                        consumeRecord.setConsumeDate(DateUtil.lastMonth());
                        //剩余未还金额
                        consumeRecord.setMoney(c.getMoney().subtract(subtract));
                        consumeRecord.setId(c.getId());
                        consumeRecord.setRates(new BigDecimal(0));
                        b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                        if (!b) {
                            throw new RuntimeException("还款失败");
                        }
                    }
                }
            } else {
                //如果未超过30天，则直接还取本金即可
                //判断可以还清，还是需要拆分订单
                ConsumeRecord consumeRecord = new ConsumeRecord();
                consumeRecord.setRates(new BigDecimal(0));
                consumeRecord.setCardNum(c.getCardNum());
                consumeRecord.setConsumeDate(c.getConsumeDate());
                consumeRecord.setMoney(c.getMoney());
                if (c.getMoney().compareTo(creditCardVo.getMoney()) <= 0) {
                    //则证明还款金额大于订单金额，直接还款
                    //设置当前订单为还清状态
                    //将id带走，则执行修改语句
                    consumeRecord.setId(c.getId());
                    consumeRecord.setRemark(1);
                    boolean b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                    if (!b) {
                        throw new RuntimeException("还款失败");
                    }
                    //当前传进来的还款金额=还款金额-(当前订单本金+利息)
                    creditCardVo.setMoney(creditCardVo.getMoney().subtract(c.getMoney()));
                } else {
                    //生成完成订单
                    IdWorker idWorker = new IdWorker();
                    consumeRecord.setId(idWorker.nextId());
                    //还款金额为用户当前还款金额的剩余部分
                    consumeRecord.setMoney(creditCardVo.getMoney());
                    consumeRecord.setRemark(1);
                    boolean b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                    if (!b) {
                        throw new RuntimeException("还款失败");
                    }
                    //修改未缴纳的订单
                    consumeRecord.setId(c.getId());
                    consumeRecord.setRemark(0);
                    consumeRecord.setMoney(c.getMoney().subtract(creditCardVo.getMoney()));
                    b = this.consumeRecordService.saveOrUpdate(consumeRecord);
                    if (!b) {
                        throw new RuntimeException("还款失败");
                    }
                    creditCardVo.setMoney(new BigDecimal(0));
                }
            }
        }
        //for循环结束之后如果还有剩余的资金，重新打回原来的银行卡中
        if (creditCardVo.getMoney().compareTo(new BigDecimal(0))>0){
            //则证明还款金额已超过最大欠款金额，打回原来的银行卡中
            arBankcard.setBalance(arBankcard.getBalance().add(creditCardVo.getMoney()));
            log.info("-----------------------------此时还款多余的金额剩余多少----------->{}",creditCardVo.getMoney());
            this.bankCardFeignService.update(arBankcard);
            return Result.build(null, 200,"如有还款额过大，则1-2小时内会返还到原来的卡中");
        }
        return Result.build(null, ResultCodeEnum.SUCCESS);
    }

    @Override
    public Result borrowMoney(CreditCardVo creditCardVo) {
        log.info("creditCardVo-------->{}", creditCardVo);
        if (ObjectUtil.isEmpty(creditCardVo)) {
            return Result.build(null, ResultCodeEnum.DATA_ERROR);
        }
        //首先判断是否是用户本人的操作
        //通过传入的cid去查找当前信用卡信息
        CreditCard creditCard = this.creditCardMapper.selectById(creditCardVo.getCid());
        log.info("当前用户所要提现的信用卡-----------row:(234)》{}", creditCard);
        //并将uid所在的用户表中找寻用户的真实姓名与用户输入的vo类中的用户id进行匹配
        ArUser arUser = this.userFeignCreditCardService.getUserById(creditCard.getUid()).getData();
        log.info("当前信用卡所属的用户信息-------------row:(237)》{}", arUser);
        //用户表中的真实姓名与vo类中的真实姓名一致，并且银行卡中的真实姓名与用户表中的一致则证明是该用户本人的操作
        if (!arUser.getRealName().equals(creditCardVo.getRealName())) {
            return Result.build("输入信息有误", ResultCodeEnum.DATA_ERROR);
        }
        //判断是否是本人操作
        ArBankcard arBankcard = this.bankCardFeignService.getCreditCardById(creditCardVo.getBid()).getData();
        log.info("查询当前用户想要转出的银行卡信息--------------row:(244)》{}", arBankcard);
        if (!arBankcard.getRealName().equals(creditCardVo.getRealName())) {
            return Result.build("输入信息有误", ResultCodeEnum.DATA_ERROR);
        }
        //取现操作，首先判断转出金额是否小于信用卡透支金额
        if (creditCardVo.getMoney().compareTo(creditCard.getAllCount().subtract(creditCard.getBorrowCount())) > 0) {
            //提现金额大于银行卡可借款金额，取现失败
            log.info("信用卡剩余可用金额是否能够支持本次的提现----->{}", creditCardVo.getMoney().compareTo(creditCard.getAllCount().subtract(creditCard.getBorrowCount())));
            return Result.build("可取现金额不足", ResultCodeEnum.DATA_ERROR);
        }
        //信用卡的转出金额
        //修改信用卡透支金额
        creditCard.setBorrowCount(creditCard.getBorrowCount().add(creditCardVo.getMoney()));
        int i = this.creditCardMapper.updateById(creditCard);
        if (i != 1) {
            throw new RuntimeException("取现失败");
        }
        //银行卡的转入操作
        BigDecimal bigDecimal = null;
        //用户提现的手续费直接在银行卡中扣取
        bigDecimal = creditCardVo.getMoney().multiply(new BigDecimal(0.01));
        arBankcard.setBalance(arBankcard.getBalance().add(creditCardVo.getMoney().subtract(bigDecimal)));
        this.bankCardFeignService.update(arBankcard);
        //其次将信用卡的钱转出来，并在consume_record表新增一条借款记录
        ConsumeRecord consumeRecord = new ConsumeRecord();
        IdWorker idWorker = new IdWorker();
        consumeRecord.setId(idWorker.nextId());
        consumeRecord.setConsumeDate(new Date());
        consumeRecord.setRates(new BigDecimal(0));
        consumeRecord.setMoney(creditCardVo.getMoney());
        consumeRecord.setCardNum(creditCard.getCardNum());
        boolean b = this.consumeRecordService.saveOrUpdate(consumeRecord);
        if (!b) {
            throw new RuntimeException("取现失败");
        }
        return Result.build(null, ResultCodeEnum.SUCCESS);
    }

    @Override
    public Result getBankCardByCardNum(String cardNum) {
        log.info("cardNum-------->{}",cardNum);
        ArBankcard bankCardByCardNum = this.bankCardFeignService.getBankCardByCardNum(cardNum);
        if (ObjectUtil.isEmpty(bankCardByCardNum)) {
            return Result.build("银行卡号不正确！", 500,"银行卡号不正确！");
        }
        return Result.build(bankCardByCardNum, ResultCodeEnum.SUCCESS);
    }


}




